home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 2
/
Gold Medal Software Volume 2 (Gold Medal) (1994).iso
/
prog
/
asm_n_z.arj
/
SWAP.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-08-09
|
86KB
|
2,079 lines
page 60,132
;Preamble:
; Received this file from Raymond Michels, author of "Undocumented
;DOS Internals," (1989 Programmers' Journal 7.2, pp. 32-37).
; Although it makes no mention, it clearly builds on TSRDEMO2.ASM
;that is authored by Thomas Brandenborg (Lundbyesgade 11, DK-8000
;Aarhus C., DENMARK) which is shareware found on Channel 1 BBS (617-354-
;8873, Boston)
; There are a few notable differences between the two:
; 1. DEMO only went so far as to popup a message while this program
; will free 192K of memory used by the current application and then run
; another program in that space (NOTE: this program should not be;
; confused with SWAP being distributed as SWAPIT.ZIP by Nico Mak,
; Mansfield Software Group, Storrs,CT and described in Dr. Dobbs
; April, 1989)
; 2. SWAP handles screen storage and restoration
; 3. SWAP adds scrolldown and scrollup in SCAN Codes
; 4. SWAP adds an sti (opcode) in OurInt13 just after the
; call OldInt13
; 5. SWAP adds a deinstall TSR routine
; 6. SWAP modifies handling of ^C with addition of PrevInt23 variable
; 7. SWAP changes messages in general
; 8. SWAP adds routine CLS to clear screen
; 9. SWAP adds saving of screen mode (@ IsColor:)
; 10. SWAP removed treatment of EnvSeg at very end
;
; W. Curtiss Priest, Humanic Systems
; Lexington, MA, 02173
;
; CONTEXT SWITCH
;==============================================================================
; DEFINE BIOS DATA SEGMENT OFFSETS
;==============================================================================
BiosData segment at 40h
org 17h
KbFlag label byte ;current shift status bits
org 18h
KbFlag1 label byte ;current key status of toggle keys
BiosData ends
;==============================================================================
; DEFINE OFFSETS WITHIN BIOS EXTRA DATA SEG
;==============================================================================
BiosXX segment at 50h
org 0
StatusByte label byte ;PrtSc status
BiosXX ends
ErrPrtSc equ -1 ;err during last PrtSc
InPrtSc equ 1 ;PrtSc in progress
;==============================================================================
; DEFINE OFFSETS WITHIN OUR PSP
;==============================================================================
Cseg segment byte public
org 2
TopSeg label word ;last seg in alloc block
org 2ch
EnvSeg label word ;seg of our environment copy
Cseg ends
;==============================================================================
; DOS COM-FILE ENTRY POINT
;==============================================================================
Cseg segment public byte
assume cs:Cseg, ds:nothing, es:nothing, ss:nothing
org 100h
ComEntry: jmp Init ;JMP to init at bottom of seg
;==============================================================================
; IDENTIFICATION CODES FOR THIS TSR (MUST BE UNIQUE FOR EACH CO-EXISTING TSR)
; HIGH BYTE OF GetId MUST NOT MATCH ANY AH REQUEST CODES FOR INT16H.
;==============================================================================
GetId equ "bn" ;INT16h AX val to get MyId
MyId equ "BN" ;ID of this TSR
;==============================================================================
; FLAGS AND PTRS FOR RESIDENT HANDLING
;==============================================================================
TsrMode db 0 ;bits for various modes
InInt08 equ 1 SHL 0 ;timer0 tick handler
InInt09 equ 1 SHL 1 ;keyboard handler
InInt13 equ 1 SHL 2 ;BIOS disk I/O
InInt28 equ 1 SHL 3 ;INT28 handler
In28Call equ 1 SHL 4 ;we have issued INT28
InPopup equ 1 SHL 5 ;popup routine activated
NewDos equ 1 SHL 6 ;DOS 2.x in use
InDosClr equ 1 SHL 7 ;InDos 0 at popup time
KeyMode db 0 ;bits for hotkey status
HotIsShift equ 1 SHL 0 ;hotkey is shift state
InHotMatch equ 1 SHL 1 ;so far keys match hotkey seq
HotKeyOn equ 1 SHL 2 ;full hotkey pressed
InDosPtr label dword ;seg:off of InDos flag
InDosOff dw 0
InDosSeg dw 0
CritErrPtr label dword ;seg:off of CritErr flag
CritErrOff dw 0
CritErrSeg dw 0
GetOutFlag dw 0 ;set to cause de-install
;==============================================================================
; DATA FOR INT09H HANDLER TO CHECK FOR HOTKEY COMBINATION
;==============================================================================
; ------------ EQU'S FOR BIT SHIFTS WITHIN KEYBOARD FLAGS
InsState equ 80h
CapsState equ 40h
NumState equ 20h
ScrollState equ 10h
AltShift equ 08h
CtlShift equ 04h
LeftShift equ 02h
RightShift equ 01h
InsShift equ 80h
CapsShift equ 40h
NumShift equ 20h
ScrollShift equ 10h
HoldState equ 08h
; ------------ SCAN CODES FOR VARIOUS SHIFT KEYS
LeftDown equ 42 ;scan code of left shift key
LeftUp equ LeftDown OR 80h
RightDown equ 54 ;scan code of right shift key
RightUp equ RightDown OR 80h
AltDown equ 56 ;scan code of alt key
AltUp equ AltDown OR 80h
CtlDown equ 29 ;scan code of ctrl key
CtlUp equ CtlDown OR 80h
ScrollDown equ 70 ;scan code of scroll lock
ScrollUp equ ScrollDown OR 80h
; ------------ MISC KEYBOARD DATA
KbData equ 60h ;keyboard data input
;==============================================================================
; TO USE A SHIFT KEY COMBINATION AS HOT KEY:
; - SET THE FLAG HotIsShift IN KeyMode
; - DEFINE THE SHIFT STATUS BITS IN THE VARIABLE HotKeyShift
;
; TO USE A SERIES OF SCAN CODES AS HOT KEY:
; - CLEAR THE FLAG HotIsShift IN KeyMode
; - INSERT THE MAKE AND BREAK SCAN CODES IN THE HotKeySeq STRING
; NOTE: WITH THIS IMPLEMENTATION YOU SHOULD NOT USE A HOT KEY
; SEQUENCE WHICH PRODUCES A KEY IN THE BIOS KEYBOARD QUEUE,
; SINCE THE KEY IS NOT REMOVED BEFORE CALLING THE POPUP ROUTINE.
;
; NOTE: HOTKEY TYPE AND CONTENTS OF HOTKEY VARIABLES MAY BE CHANGED AT RUN TIME
;==============================================================================
HotKeyShift db AltShift OR RightShift ;shift state IF HotIsShift=FF
HotKeySeq db AltDown,RightDown
HotKeyLen equ $-HotKeySeq
HotIndex db 0 ;# key in seq to compare next
BetweenKeys db 0 ;timeout count between keys
KeyTimeOut equ 10 ;more ticks means not a hotkey
;==============================================================================
; DATA FOR INT08H HANDLER TO CHECK FOR POPUP
;==============================================================================
SafeWait db 0 ;count-down for safe popup
MaxWait equ 8 ;wait no more 8/18 sec
;==============================================================================
; PROCESS & SYSTEM DATA
;==============================================================================
OurSS dw 0 ;stack for popup routine
OurSP dw 0
StackSize equ 512 ;bytes to reserve for stack
OldSS dw 0 ;old stack seg
OldSP dw 0 ;old stack off
OurPSP dw 0 ;our PSP seg
OldPSP dw 0 ;old PSP seg
OldDTA label dword ;seg:off of old DTA area
OldDTAOff dw 0
OldDTASeg dw 0
OurDTA label dword ;seg:off of our DTA
OurDTAOff dw 0
OurDTASeg dw 0
OldBreak db 0 ;old ctrl-break state
OldExtErr dw 3 dup (0) ;AX,BX,CX of ext err
;==============================================================================
; LOCATIONS FOR SAVED INTERRUPT VECTORS
;==============================================================================
OldInt08 label dword ;Timer0 loaded before this
OldInt08Off dw 0
OldInt08Seg dw 0
OldInt09 label dword ;Kb handler loadde before this
OldInt09Off dw 0
OldInt09Seg dw 0
OldInt13 label dword ;BIOS diskette I/O
OldInt13Off dw 0
OldInt13Seg dw 0
OldInt16 label dword ;BIOS kb Q-handler
OldInt16Off dw 0
OldInt16Seg dw 0
OldInt1B label dword ;^break of process we steal
OldInt1BOff dw 0
OldInt1BSeg dw 0
OldInt1C label dword ;timer tick of process we steal
OldInt1COff dw 0
OldInt1CSeg dw 0
OldInt21 label dword ;DOS function dispatcher
OldInt21Off dw 0
OldInt21Seg dw 0
PrevInt23 label dword ;^C of process we steal
PrevInt23Off dw 0
PrevInt23Seg dw 0
OldInt23 label dword ;^C internal handler (IRET)
OldInt23Off dw 0
OldInt23Seg dw 0
OldInt24 label dword ;crit err of process we steal
OldInt24Off dw 0
OldInt24Seg dw 0
OldInt28 label dword ;DOS idles loaded before this
OldInt28Off dw 0
OldInt28Seg dw 0
ApplPSP dw 0 ;stores application PSP
CurrentAlloc dw 0 ;application memory allocation
;==============================================================================
; SPEAKER/TONE GENERATION DATA
;==============================================================================
PB0port equ 61h ;port for speaker bit
ErrLen1 equ 10 ;# outer err beep cycles
ErrLen2 equ 80 ;# inner err beep cycles
ErrLow equ 100 ;low tone wait in err beep
ErrHi equ 40 ;hi tone wait in err beep
;==============================================================================
; ErrBeep - PRODUCE ERROR-INDICATING SOUND ON SPEAKER
;==============================================================================
ErrBeep proc near
assume ds:nothing, es:nothing, ss:nothing
push ax ;save regs used
push bx
push cx
push dx
mov cx,ErrLen1 ;# mix-cycles for beep
ErrBeep1: mov dx,ErrLow ;wait time for half-cycle
mov bx,ErrLen2 ;len of one tone
call DoTone ;output low err tone
mov dx,ErrHi ;wait time for half-cycle
mov bx,ErrLen2 ;len of one tone
call DoTone ;output low err tone
loop ErrBeep1 ;loop for some time
pop dx
pop cx ;restore regs
pop bx
pop ax
ret
ErrBeep endp
;==============================================================================
; DoTone - OUTPUT ONE TONE ON THE SPEAKER
;
; INPUT: DX: LOOP WAIT TIME FOR HALF CYCLE IN TONE
; BX: NUMBER OF CYCLES FOR TONE DURATION
; OUTPUT: NONE
; REGS: ALL PRESERVED
;==============================================================================
DoTone proc near
assume ds:nothing, es:nothing, ss:nothing
push ax ;save regs used
push bx
push cx
in al,PB0port ;get PB0 reg pattern
mov ah,al ;save it
DoTone1: and al,0fch ;mask off speaker bit
out PB0port,al ;pull!
mov cx,dx ;half cycle in counter
DoTone2: loop DoTone2 ;leave there for half a cycle
or al,2 ;turn on speaker bit
out PB0port,al ;push!
mov cx,dx ;half cycle in counter
DoTone3: loop DoTone3 ;leave there for half a cycle
dec bx ;count down tone duration
jnz DoTone1 ;go through full tone
mov al,ah ;AL=original PB0 reg value
out PB0port,al ;restore
pop cx ;restore regs
pop bx
pop ax
ret
DoTone endp
;==============================================================================
; TestSafe - CHECK IF THIS IS A SAFE TIME TO DO A POP UP
;
; RETURN CLC IF SAFE TO POP UP, CY IF NOT SAFE.
;
; CHECK IF ANY INTs ARE IN CRITICAL AREAS (InInt09 & InInt13)
; CHECK IF WE ARE IN AN OUR OWN INT28 CALL (In28Call)
; CHECK 8259A PIC ISR REGISTER FOR MISSING EOIs
; CHECK IF DOS IS STABLE FOR POP UP
; CHECK IF A PRINT SCREEN IS IN PROGRESS
;==============================================================================
TestSafe proc near
assume ds:nothing, es:nothing
push ax ;save regs used
push bx
push ds
; ------------ CHECK INTs TO SEE IF THEY WERE INTERRUPTED AT BAD TIMES
test TsrMode,InInt09 OR InInt13 OR In28Call
jnz NotSafe ;jump if any INTs are chopped
; ------------ CHECK THE 8259A PIC ISR REGISTER FOR NON-EOIed HW INTs
mov al,00001011b ;tell 8259A we want the ISR
out 20h,al ;8259A command reg
nop
nop
nop ;now, ISR should be ready
in al,20h ;AL=mask of active INTs
or al,al ;test all (IRQ0 *did* EOI)
jnz NotSafe ;jump if active INTs
; ------------ NOW, ENSURE THAT DOS WAS NOT INTERRUPTED
assume ds:nothing
lds bx,InDosPtr ;now, DS:BX=InDos
mov al,byte ptr [bx] ;get InDos to AL
lds bx,CritErrPtr ;now, DS:BX=CritErr
or al,byte ptr [bx] ;both flags zero?
jz DosSafe ;YES - DOS is really idle
test TsrMode,InInt28 ;is this an INT28h
jz NotSafe ;NO - not safe, should be idle
cmp al,1 ;YES - one InDos entry only?
ja NotSafe ;NO - jump if more than one
DosSafe:
; ------------ CHECK TO SEE IF A PRINT SCREEN IS IN PROGRESS
mov ax,BiosXX
mov ds,ax ;move DS to BIOS extra data seg
assume ds:BiosXX
cmp StatusByte,InPrtSc ;print screen in progress?
je NotSafe ;YES - jump if prtsc
; ------------ SEEMS TO BE A SAFE TIME FOR POPUP
IsSafe: clc ;CLC=safe to popup
jmp short ExitSafe ;end this then
; ------------ APPARENTLY THIS IS JUST NOT THE TIME TO DO A POPUP
NotSafe: stc ;CY=don't popup now
; ------------ RETURN TO CALLER WITH CARRY SET/CLEAR
ExitSafe: pop ds ;restore regs
pop bx
pop ax
ret
TestSafe endp
;==============================================================================
; OurInt08 - TSR INT08H HANDLER TO WATCH FOR HOTKEY AND SAFE POPUP TIMES
;
; CALL OldInt08
; CHECK FOR RE-ENTRANCE INTO CRITICAL INT08 CODE
; SET InInt08 FLAG
; CHECK FOR TIMEOUT BETWEEN KEYS IN HOTKEY SEQUENCE
; CHECK IF HOTKEY WAS PRESSED
; CHECK IF ALREADY InPopup OR InInt28
; CHECK IF SAFE TIME FOR SYSTEM TO POPUP
; UPDATE FLAGS AND CALL POPUP IF SAFE
; GIVE ERROR BEEP IF POPUP WAS UNSAFE FOR A LONG TIME
; RESET InInt08 FLAG
; DO IRET
;==============================================================================
; ------------ NEAR JUMP DESTINATION FOR FAST IRET'S
Exit08: iret ;IRET (!)
; ------------ ACTUAL INT08 ENTRY POINT
OurInt08 proc far
assume ds:nothing, es:nothing, ss:nothing
pushf ;simulate INT08
cli ;in case others forgot it
call OldInt08 ;call TSRs loaded before us
; ------------ ENSURE NO RECURSION INTO CRITICAL INT08 CODE
sti ;we'll manage INTs
test TsrMode,InInt08 ;already in here somewhere?
jnz Exit08 ;YES - don't re-enter
or TsrMode,InInt08 ;tell people we are here
push ax ;need a few regs in this code
; ------------ COUNT DOWN TIME-OUT BETWEEN KEYS IN HOTKEY SEQUENCE
test KeyMode,InHotMatch ;are we in a key match?
jz TestHot08 ;NO - don't care then
dec BetweenKeys ;count down timeout val
jnz TestHot08 ;jump if no timeout yet
mov HotIndex,0 ;start match from beginning
and KeyMode,not InHotMatch ;just so we know it next time
; ------------ CHECK FOR POSSIBLE POPUP ACTIONS
TestHot08: test KeyMode,HotKeyOn ;has hotkey been pressed?
jz ExitInt08 ;NO - jump if no fun here
test TsrMode,InInt28 OR InPopup
jnz ExitInt08 ;jmp if not alr in business
; ------------ HOTKEY PRESSED, CHECK TO SEE IF IT IS SAFE TO POPUP
cmp SafeWait,0 ;first time we find hotkey?
ja TestSafe08 ;NO - wait has alr been set
mov SafeWait,MaxWait ;# ticks to wait at most
TestSafe08: call TestSafe ;now, CY clear if popup is safe
jc NotSafe08 ;jump if popup is bad idea
; ------------ SEEMS SAFE TO POPUP AT THIS TIME, SO DO!
xor al,al ;fast zero
mov SafeWait,al ;don't count any more
and KeyMode,not HotKeyOn ;clear hotkey status
or TsrMode,InPopup ;tell'em we enter popup routine
and TsrMode,not InInt08 ;OK to enter critical INT08
call InitPopup ;do actual popup
or TsrMode,InInt08 ;back in INT08 code here
and TsrMode,not InPopup ;not in popup code any more
mov SafeWait,al ;in case of hotkey during popup
and KeyMode,not HotKeyOn ;clear hotkey status
jmp short ExitInt08 ;finally done
; ------------ UNSAFE POPUP TIME, COUNT DOWN SafeWait
NotSafe08: dec SafeWait ;count down waiter
jnz ExitInt08 ;jump if still no timeout
; ------------ NO SAFE TIMES FOUND FOR QUITE SOME TIME, ERROR
and KeyMode,not HotKeyOn ;might as well clear hotkey
call ErrBeep ;do an error beep
; ------------ NORMAL INT08H EXIT, RESET InInt08
ExitInt08: pop ax ;restore regs used
and TsrMode,not InInt08 ;clear that flag
iret ;straight back
OurInt08 endp
;==============================================================================
; OurInt09 - TSR INT09H HANDLER TO WATCH FOR HOTKEY
;
; SAVE SCAN CODE
; CALL OldInt09
; CHECK FOR RECURSION INTO CRITICAL INT09 CODE
; SET InInt09 FLAG
; CHECK IF HOTKEY ALREADY SET
; DETERMINE HOTKEY TYPE (SHIFT STATE OR KEY SEQENCE)
; CHECK SHIFT STATE IF HotIsShift
; COMPARE FOR KEY MATCH IF (NOT HotIsShift)
; SET HotKeyOn IF HOTKEY PRESSED
; RESET InInt09 FLAG
; DO IRET
;==============================================================================
; ------------ NEAR JUMP DESTINATION FOR EARLY EXITS
Exit09: pop bx ;restore regs
pop ax
iret ;flags restored from stack
; ------------ ACTUAL INT09 ENTRY POINT
OurInt09 proc far
assume ds:nothing, es:nothing, ss:nothing
push ax ;save regs used
push bx
; ------------ READ SCAN CODE, IN CASE SEQUENCE MATCHING SELECTED
in al,KbData ;Al=key, preserved by BIOS
; ------------ CALL BIOS TO PERFORM ITS DUTIES
pushf ;simulate INT (CLI alr set)
cli ;in case others forgot it
call OldInt09 ;call BIOS/earlier TSRs
; ------------ ENSURE NO RECURSION INTO CRITICAL INT09 CODE
sti ;we'll manage INTs
test TsrMode,InInt09 ;alr in business?
jnz Exit09 ;YES - skip test till clear
or TsrMode,InInt09 ;tell them we arrived here
; ------------ DETERMINE HOT KEY TYPE SELECTED
test KeyMode,HotKeyOn ;already hotkey there?
jnz ExitInt09 ;YES - no double hotkeys here
test KeyMode,HotIsShift ;shift state type hotkey?
jz CompSeq09 ;NO - go compare sequence
; ------------ COMPARE CURRENT SHIFT STATUS AGAINST HOTKEY
push ds ;save current ds
mov ax,BiosData ;move DS to BIOS data seg
mov ds,ax ;DS can now access keyb vars
assume ds:BiosData ;tell MASM about our DS
mov al,KbFlag ;get BIOS shift state bits
pop ds ;restore
assume ds:nothing ;last thing we know about him
and al,HotKeyShift ;isolate relevant bits
cmp al,HotKeyShift ;our shift state in effect?
jne ExitInt09 ;NO - not that shift state
or KeyMode,HotKeyOn ;YES - flag hotkey
jmp short ExitInt09 ;now we can be proud to leave
; ------------ MATCH KEY IN SCAN CODE SEQUENCE
CompSeq09: mov bl,HotIndex ;next scan code to match
xor bh,bh ;must be word
cmp al,HotKeySeq[bx] ;does key match?
je HotMatch09 ;YES - jump if match
mov HotIndex,bh ;search from start next time
and KeyMode,not InHotMatch ;current no match
jmp short ExitInt09 ;now end this
; ------------ KEY MATCHED...NEXT SCAN CODE IN HotKeySeq
HotMatch09: inc bl ;new code at next pass
cmp bl,HotKeyLen ;did we match whole sequence?
jae HotHit09 ;YES - jump if full sequence
mov HotIndex,bl ;NO - save new count
mov BetweenKeys,KeyTimeOut ;reset counter between keys
or KeyMode,InHotMatch ;we are in a match now
jmp short ExitInt09 ;time to end this
; ------------ KEY MATCHED ALL SCAN CODES IN HOTKEY SEQUENCE
HotHit09: or KeyMode,HotKeyOn ;say hotkey was pressed
mov HotIndex,bh ;match 1st code next time
and KeyMode,not InHotMatch ;that's the end of a match
; ------------ EXIT FROM INT09H, RESET InInt09 FLAG
ExitInt09: and TsrMode,not InInt09 ;tell'em we left this code
pop bx ;restore regs
pop ax
iret ;flags restored from stack
OurInt09 endp
;==============================================================================
; OurInt13 - SET InInt13 FLAG TO SAY THAT WE ARE IN AN INT13H
;==============================================================================
OurInt13 proc far
assume ds:nothing, es:nothing, ss:nothing
pushf ;save flags we use
or TsrMode,InInt13 ;remember we are in BIOS now
popf ;restore flags
pushf ;simulate INT13
cli ;just in case others forgot
call OldInt13 ;let BIOS handle it all
sti ;turn 'em back on
pushf ;BIOS uses flag return
and TsrMode, not InInt13 ;tell people we left INT13h
popf
ret 2 ;throw flags off stack
OurInt13 endp
;==============================================================================
; OurInt16 - TSR INT16H HANDLER, INT28 CHAIN INTERFACE
;
; INPUT: AX = GetId
; OUTPUT: AX = MyId
; REGS: AX LOST, ALL OTHERS PRESERVED
; DESCRIPTION: DETERMINE IF TSR WITH THIS ID IS ALREADY IN MEMORY
;
; INPUT: AH = 00
; OUTPUT: AX = NEXT KEY FROM BUFFER
; REGS; AX LOST, ALL OTHERS PRESERVED
; DESCRIPTION: RETURN A KEY FROM KEYBOARD BUFFER, WAIT TILL KEY IS PRESSED
;
; INPUT: AH = 02
; OUTPUT: AX = KEY FROM BUFFER IN ANY
; ZF = NO KEYS IN BUFFER (AX PRESERVED)
; NZ = KEY IN BUFFER (RETURNED IN AX, KEY STILL IN BUFFER)
; DESCRIPTION: CHECK BUFFER FOR ANY PENDING KEYS, RETURN KEY IF ANY
;
; NOTE: ALL OTHER AX REQUEST CODES ARE PASSED ON TO BIOS INT16H HANDLER.
;
; NOTE: DURING INT28 POPUP (InPopup AND NOT InDosClr) FUNCTIONS AH=0 AND
; AH=1 WILL ISSUE INT28, UNLESS InDos HAS FROM VALUE AT POPUP OR
; CritErr HAS BEEN SET.
;==============================================================================
OurInt16 proc far
assume ds:nothing, es:nothing, ss:nothing
sti ;we'll manage INTs
pushf ;save callers flags
cmp ax,GetId ;return ID request?
jne NotId16 ;NO - jump if not
; ------------ TSR DIAGNOSTIC REQUEST, RETURN SPECIAL VALUE TO SAY WE ARE HERE
mov ax,MyId ;ID val returned in AX
popf ;restore flags
iret ;return to caller
; ------------ PASS CONTROL TO BIOS, FLAGS ON STACK
GoBios16: popf ;restore flags at INT time
jmp OldInt16 ;continue in the woods
; ------------ REGULAR BIOS INT16 REQUEST, CHECK FOR ANY FANCY ACTIONS
NotId16: test TsrMode,InPopup ;are we in a popup?
jz GoBios16 ;NO - leave rest with BIOS
test TsrMode,InDosClr ;InDos clear at popup?
jnz GoBios16 ;YES - no need to signal INT28
popf ;restore original flags
push bx ;we need a few regs here
push cx
push si
push ds
pushf ;original flags back on stack
; ------------ GET REQUEST CODE TO BH ENHANCED BIT TO BL
mov bh,ah ;BH=function request code
and bh,not 10h ;zap enhanced kybd bit
cmp bh,1 ;any function above 1?
ja ExitBios16 ;YES - leave rest with BIOS
mov bl,ah ;BL used for enhanced bit
and bl,10h ;BL=value of enhanced bit
; ------------ GET InDos To CL, CritErr to CH, SETUP REGS
assume ds:nothing
lds si,InDosPtr ;DS:[SI]=InDos
mov cl,byte ptr [si] ;CL=InDos value
lds si,CritErrPtr ;ES:[SI]=CritErr
mov ch,byte ptr [si] ;CH=CritErr value
mov si,ax ;save AX call value
mov ax,cs ;move DS here, now we got it
mov ds,ax
assume ds:Cseg ;everybody should know
; ------------ CHECK KEYBOARD BUFFER, ORIGINAL FLAGS ON STACK
Wait16: mov ah,1 ;AH=1=test buffer status
or ah,bl ;maintain enhanced bit value
popf ;restore original flags
pushf ;simulate INT
cli ;in case others forgot
call OldInt16 ;now, ZF set if no keys
pushf ;save result flags
jnz TestSkip16 ;jump if a key was found
; ------------ NO KEY FOUND, CALL INT28 IF DOS InDos ALLOWS
cmp cx,0001h ;CritErr=0, InDos=1 ?
jne NextKey16 ;NO - wait for next key
or TsrMode,In28Call ;tell people we called this INT
int 28h ;now take your chance
and TsrMode,not In28Call ;end of that call
; ------------ TEST BUFFER AGAIN IF INT16.00, IRET IF INT16.01
NextKey16: or bh,bh ;is this a wait for key?
jz Wait16 ;YES - then go wait for it!
mov ax,si ;restore original AX contents
jmp short Exit16 ;NO - exit with status we got
; ------------ KEY IN BUFFER, IF CTRL-C WE MAY HAVE TO SKIP IT, FLAGS ON STACK
TestSkip16: cmp al,3 ;is this Ctrl-C?
jne TestExit16 ;NO - determine exit method
test cx,not 0001h ;anything but InDos=1?
jz TestExit16 ;NO - determine exit method
; ------------ SKIP CTRL-C IN KEYBOARD BUFFER
mov ah,bl ;AH=0 + enhanced bit
popf ;restore original INTs
pushf ;save again
pushf ;simulate INT
cli ;simulate properly!
call OldInt16 ;now, key should be gone
jmp short Wait16 ;do as if nothing had happened
; ------------ KEY IN AX, IRET IF INT16.01, LEAVE WITH BIOS IF INT16.00
TestExit16: or bh,bh ;is this a wait for key?
jnz Exit16 ;NO - do fast return
mov ax,si ;YES - restore AX code
; ------------ PASS CONTROL TO BIOS, FLAGS & REGS ON STACK
assume ds:nothing
ExitBios16: popf ;restore work flags
pop ds ;restore regs
pop si
pop cx
pop bx
cli ;should look like an INT
jmp OldInt16 ;leave rest with BIOS
; ------------ RETURN FROM INT16, FLAGS & REGS ON STACK
assume ds:nothing
Exit16: popf ;restore proper flags
pop ds ;restore regs
pop si
pop cx
pop bx
ret 2 ;IRET, without flags restore
OurInt16 endp
;==============================================================================
; OurInt21 - INT21 FILTER TO THROW DANGEROUS DOS CALLS ON CRITICAL STACK
;
; CHECK IF InPopup AND InDosClr
; CHECK FUNCTION USES CONSOLE STACK
; SET CritErr IN DOS IF CONSOLE STACK USED
; CALL OldInt21
; RESTORE CritErr IF CRITICAL STACK USED
;==============================================================================
OurInt21 proc far
assume ds:nothing, es:nothing
pushf ;save calling flags
sti
test TsrMode,InPopup ;are we in a popup?
jz GoDos21 ;NO - don't worry then
test TsrMode,InDosClr ;console stack idle?
jnz GoDos21 ;YES - nothing fancy then
; ------------ THIS IS 2ND CALL INTO DOS, SEE IF USING CONSOLE STACK
cmp ah,0ch ;any function 00-0C?
jbe UseCrit21 ;YES - use critical stack
test TsrMode,NewDos ;NO - is this DOS 3.x?
jnz GoDos21 ;YES - no other to worry about
cmp ah,50h ;set PSP function?
je UseCrit21 ;YES - use critical stack
cmp ah,51h ;get PSP function?
jne GoDos21 ;NO - leave it with DOS
; ------------ FORCE USE OF CRITICAL STACK FOR THIS CALL
UseCrit21: assume ds:nothing ;nothing to say about DS
push si ;save regs
push ds
lds si,CritErrPtr ;now, DS:[SI]=InDos
mov byte ptr [si],-1 ;FF=use crit stack now
pop ds ;restore regs
pop si
popf ;retsore flags setting
pushf ;simulate INT
cli ;in case others forgot
call OldInt21 ;flags already on stack
push si ;save regs
push ds
lds si,CritErrPtr ;now, DS:[SI]=InDos
mov byte ptr [si],0 ;0=back to default stack
pop ds ;restore regs
pop si
ret 2 ;IRET throw old flags
; ------------ PASS CONTROL TO DOS, FLAGS ON STACK
GoDos21: popf ;restore original flags
cli ;just in case someone forgot
jmp OldInt21 ;let DOS handle the rest
OurInt21 endp
;==============================================================================
; OurInt24 - SAFE DOS CRITICAL ERROR HANDLER
;
; IF DOS 3.X, FAIL THE SYSTEM CALL
; IF NOT DOS 3.X, IGNORE ERROR
;==============================================================================
OurInt24 proc far
assume ds:nothing, es:nothing, ss:nothing
mov al,3 ;AL=3=fail system call
test TsrMode,NewDos ;are we using DOS 3.x?
jnz Exit24 ;YES - OK to use AL=3
xor al,al ;NO - have to ignore err then
Exit24: iret ;return to DOS
OurInt24 endp
;==============================================================================
; OurInt28 - TSR INT28H HANDLER, ALLOWS POPUP DURING DOS IDLE CALLS
;
; CALL OldInt28
; CHECK FOR RECURSION INTO CRITICAL INT28 CODE (& OTHER INTs AS WELL)
; SET InInt28 FLAG
; CHECK FOR HOTKEY
; CHECK IF SAFE TO POPUP
; DO POPUP IF SAFE AT THIS TIME
; RESET InInt28 FLAG
; DO IRET
;==============================================================================
; ------------ NEAR JUMP DESTINATION FOR FAST IRET'S
Exit28: iret ;IRET (!)
; ------------ ACTUAL INT28 ENTRY POINT
OurInt28 proc far
assume ds:nothing, es:nothing, ss:nothing
pushf
cli ;in case others forgot it
call OldInt28 ;call TSRs loaded before this
; ------------ ENSURE NO RECURSION ON CRITICAL INT28 CODE
sti ;we'll manage INT's after this
test TsrMode,InInt08 OR InInt28 OR In28Call OR InPopup
jnz Exit28 ;exit fast if already going
or TsrMode,InInt28 ;tell'em we are here
; ------------ CHECK FOR POSSIBLE POPUP ACTIONS
test KeyMode,HotKeyOn ;any hotkeys pressed?
jz ExitInt28 ;NO - don't check any more then
; ------------ HOTKEY WAS PRESSED, ENSURE IT'S SAFE TO DO POPUP
call TestSafe ;now, CY clear if popup is OK
jc ExitInt28 ;jump if not to popup
; ------------ SEEMS OK TO DO POPUP, SO DO!
and KeyMode,not HotKeyOn ;clear hotkey status
or TsrMode,InPopup ;tell'em we enter popup routine
and TsrMode,not InInt28 ;OK to enter critical INT28
call InitPopup ;then do popup
or TsrMode,InInt28 ;back in INT28 code here
and TsrMode,not InPopup ;not in popup code any more
and KeyMode,not HotKeyOn ;clear hotkeys during popup
; ------------ NORMAL INT28H EXIT, RESET InInt28 FLAG
ExitInt28: and TsrMode,not InInt28 ;tell'em we left this code
iret ;we have nothing more to say
OurInt28 endp
;==============================================================================
; NopInt - DUMMY IRET INSTRUCTION USED BY EMPTY INT HANDLERS
;==============================================================================
NopInt: iret ;immediate return
;==============================================================================
; DE-INSTALL THIS TSR
;
; NOTE!
; ***this is only supposed to be***
; ***invoked at a DOS prompt!!!!***
;
; - RESTORE INTERRUPT VECTORS
; - GIVE BACK THE MEMORY WE STOLE
; - DO A NORMAL "4C" RETURN TO DOS
;
;==============================================================================
DeInstall proc near
push ds
mov bx,OurPSP ;restore PSP to point
mov ax,5000h ;to us
int 21h
pop ds
push ds
mov ax, 2528h
lds dx, OldInt28
int 21h ;give back dos idle
pop ds
push ds
mov ax, 2521h
lds dx, OldInt21
int 21h ;give back dos funct call
pop ds
push ds
mov ax, 2516h
lds dx, OldInt16
int 21h ;give back keyboard int
pop ds
push ds
mov ax, 2513h
lds dx, OldInt13
int 21h ;give back disk int
pop ds
push ds
mov ax, 2509h
lds dx, OldInt09
int 21h ;give back kbd handler
pop ds
push ds
mov ax, 2508h
lds dx, OldInt08
int 21h ;give back timer tick
pop ds
push ds
mov ax, 2523h
lds dx, PrevInt23
int 21h ;reset ^C handler
pop ds
push es
push cs
pop es ;free this block
mov ah, 49h
int 21h
mov es, EnvSeg
mov ah, 49h ;free environment block
int 21h
pop es
mov ax, 4C00h
int 21h
DeInstall endp
;==============================================================================
; SCREEN SAVE/RESTORE ROUTINES
;==============================================================================
scrnhold dw 2000 dup (?)
videoflag dw 0
videocard dd 0
savearea dd 0
cursorsave dw 0
cursortype dw 0
fromscr proc near
push ds ; Save DS
push es ; and ES
push bp ; and BP
mov ax, videoflag
mov dx,3dah ; Point DX to CGA status port
les di, savearea ; Dest pointer into ES:DI
mov cx, 2000 ; Length value into CX
lds si, videocard ; Source pointer into DS:SI
cld ; Set string direction to forward
cmp ax,1 ; mono?
jne .3 ; no, have to watch for flicker
rep movsw ; yes, do a fast move
jmp fromscrexit ;
.3: in al,dx ; Get 6845 status
rcr al,1 ; Check horizontal retrace
jb .3 ; Loop if in horizontal retrace: this prevents
; starting in mid-retrace, since there is
; exactly enough time for 1 and only 1 LODSW
; during horizontal retrace
cli ; No ints during critical section
.4: in al,dx ; Get 6845 status
rcr al,1 ; Check for horizontal retrace: LODSW is 1
; clock cycle slower than STOSW; because of
; this, the vertical retrace trick can't be
; used because it causes flicker! (RCR AL,1
; is 1 cycle faster than AND AL,AH)
jnb .4 ; Loop if not in retrace
lodsw ; Load the video word
sti ; Allow interrupts
stosw ; Store the video word
loop .3 ; Go do next word
fromscrexit:
pop bp ; Restore BP
pop es ; and ES
pop ds ; and DS
mov ah, 3
mov bh, 0
int 10h
mov cursorsave, dx
mov cursortype, cx
ret
fromscr endp
toscr proc near
push ds ; Save DS
push es ; and ES
push bp ; and BP
mov ax, videoflag
mov dx,3dah ; Point DX to CGA status port
les di, videocard ; Dest pointer into ES:DI
mov cx,2000 ; Length value (words) into CX
lds si, savearea ; Source pointer into DS:SI
cld ; Set string direction to forward
cmp ax,1 ; mono?
jne .0 ; no, do flicker trick
rep movsw ; yes, do it fast
jmp toscrexit
.0: lodsw ; Grab a video word
mov bp,ax ; Save it in BP
mov ah,9 ; Move horiz. + vertical retrace mask
.1: in al,dx ; Get 6845 status
rcr al,1 ; Check horizontal retrace
jb .1 ; Loop if in horizontal retrace: this prevents
; starting in mid-retrace, since there is
; exactly enough time for 1 and only 1 STOSW
; during horizontal retrace
cli ; No ints during critical section
.2: in al,dx ; Get 6845 status
and al,ah ; Check for both kinds of retrace: IF the
; video board does not report horizontal
; retrace while in vertical retrace, this
; will allow several characters to be
; stuffed in during vertical retrace
jz .2 ;
mov ax,bp ; Get the video word
stosw ; Store the video word
sti ; Allow interrupts
loop .0 ; Go do next word
toscrexit:
pop bp ; Restore BP
pop es ; and ES
pop ds ; and DS
mov dx, cs:cursorsave
mov ah, 2
mov bh, 0
int 10h
mov cx, cs:cursortype
mov ah, 1
int 10h
ret
toscr endp
;==============================================================================
; CLS---CLEAR THE SCREEN
;==============================================================================
CLS proc near ; do a BIOS screen clear
push ax
push bx
push cx
push dx
push bp
mov ax, 0600h
mov cx, 0
mov dx, 184Fh
mov bh, 07h
int 10h
pop bp
pop dx
pop cx
pop bx
pop ax
ret
CLS endp
;==============================================================================
; GOTOXY---PUT THE CURSOR SOMEWHERE
;==============================================================================
Gotoxy proc near ; row, column is already in DH, DL
push ax
push bx
mov ah, 2
mov bh, 0
int 10h
pop bx
pop ax
ret
Gotoxy endp
; --------------------------------------
; CURSORON---MAKES THE CURSOR VISIBLE.
; --------------------------------------
CursorOn proc near
mov cx, 0708h
push cx
mov ah, 0Fh
int 10h
pop cx
cmp al, 7
je COmono
COsetit:
mov ah, 1
int 10h
ret
COmono:
mov cx, 0D0Eh
jmp COsetit
CursorOn endp
;==============================================================================
; InitPopup - PREPARES SYSTEM FOR POPUP, THEN CALLS Popup, THEN RESTORES
;
; ESTABLISH INTERNAL WORK STACK
; SAVE CPU REGS
; UPDATE InDosClr FLAG WITH CURRENT VALUE OF InDos
; SAVE PROCESS RELATED SYSTEM INFO
; SAVE USER INTERRUPT VECTORS
; INSERT SAFE USER INTERRUPT VECTORS
; SAVE CURRENT SCREEN
; CALL POPUP ROUTINE
; RESTORE SCREEN
; RESTORE USER INTERRUPT VECTORS
; RESTORE PROCESS AND SYSTEM INFO
; CLEAR InDosClr FLAG TO PREVENT UNSAFE INT28 CALLs
; SEE IF DE-INSTALL SHOULD BE DONE (GetOutFlag==1)
; RESTORE CPU REGS
;==============================================================================
InitPopup proc near
assume ds:nothing, es:nothing, ss:nothing
; ------------ SWITCH TO PSP INTERNAL STACK
mov OldSS,ss ;save current stack frame
mov OldSP,sp
cli ;always CLI for the old chips
mov ss,OurSS ;move SS here
mov sp,OurSP ;move SP into position
sti ;OK guys
; ------------ SAVE ALL REGS
push ax
push bx
push cx
push dx
push bp
push si
push di
push ds
push es
mov ax,cs
mov ds,ax ;mov DS here
assume ds:Cseg ;tell MASM that
; ------------ TAG VALUE OF InDos FLAG AT TIME OF POPUP
or TsrMode,InDosClr ;assume InDos=0
les si,InDosPtr ;now, ES:[SI]=InDos
cmp byte ptr es:[si],1 ;InDos set? (>2 impossible)
jb InDosSaved ;NO - jump if all clear DOS
and TsrMode,not InDosClr ;clear flag for popup InDos
InDosSaved:
; ------------ SAVE DOS 3.X EXTENDED ERROR INFO
test TsrMode,NewDos ;really DOS 3.x?
jz Dos3Saved ;NO - jump if not 3.x
mov ah,59h ;to get err info from DOS
xor bx,bx ;BX must be zero
push ds ;save DS (killed by DOS)
int 21h ;ext err info in AX,BX,CX
pop ds ;restore
mov OldExtErr[0],ax ;save
mov OldExtErr[2],bx
mov OldExtErr[4],cx
Dos3Saved:
; ------------ SAVE CURRENT BREAK STATE, RELAX BREAK CHECKING
mov ax,3302h ;to swap DL with BREAK value
xor dl,dl ;DL=0=relax checking
int 21h ;current level in DL
mov OldBreak,dl ;save current level
; ------------ SAVE CURRENT USER INT VECTORS
mov ax,351bh ;BIOS ctrl-break int
int 21h ;ES:BX=vector
mov OldInt1BOff,bx ;save it
mov OldInt1BSeg,es
mov ax,351ch ;BIOS timer tick
int 21h ;ES:BX=vector
mov OldInt1COff,bx ;save it
mov OldInt1CSeg,es
mov ax,3523h ;DOS ctrl-C
int 21h ;ES:BX=vector
mov OldInt23Off,bx ;save it
mov OldInt23Seg,es
mov ax,3524h ;DOS crit err handler
int 21h ;ES:BX=vector
mov OldInt24Off,bx ;save it
mov OldInt24Seg,es
; ------------ INSERT DUMMY IRET INTO DANGEROUS VECTORS
mov dx,offset NopInt ;now, DS:DX=dunny iret
mov ax,251bh ;BIOS ctrlk-break handler
int 21h ;set to IRET
mov ax,251ch ;BIOS timer tick
int 21h ;set to IRET
mov ax,2523h ;DOS ctrl-C handler
int 21h ;set to IRET
; ------------ ESTABLISH SAFE CRITICAL ERROR HANDLER
mov dx,offset OurInt24 ;now, DS:DX=safe crit err
mov ax,2524h ;to set crit err handler
int 21h
; ------------ SAVE CURRENT DTA AREA, SET OUR DEFAULT DTA
mov ah,2fh ;to obtain current DTA from DOS
int 21h ;DTA addr now in ES:BX
mov OldDTAOff,bx ;save it
mov OldDTASeg,es
push ds ;save DS for a while
lds dx,OurDTA ;DS:DX=our DTA addr
mov ah,1ah ;to set DTA via DOS
int 21h ;set that addr
pop ds ;restore DS
; ------------ SAVE CURRENT PSP, ESTABLISH OURS INSTEAD
mov ax,5100h ;to get PSP from DOS
int 21h ;current PSP now in BX
mov OldPSP,bx ;save it
mov bx,OurPSP ;het our PSP instead
mov ax,5000h ;to set our PSP
int 21h
; ------------ CALL USER POPUP ROUTINE
call FromScr ;save screen
call Popup ;finally!
call ToScr ;put screen back
; ------------ RESTORE TO SAVED CURRENT PROCESS
mov bx,OldPSP ;new current process in BX
mov ax,5000h ;to set PSP via DOS
int 21h ;restore original PSP
; ------------ RESTORE SAVED DTA
push ds ;save DS for a while
lds dx,OldDTA ;DS:DX=our DTA addr
mov ah,1ah ;to set DTA via DOS
int 21h ;set that addr
pop ds ;restore DS
; ------------ RESTORE SAVED INTERRUPT VECTORS
push ds ;save for a while
assume ds:nothing ;be careful about MASM
lds dx,OldInt1B ;BIOS ctrl-break handler
mov ax,251bh
int 21h
lds dx,OldInt1C ;BIOS timer tick
mov ax,251ch
int 21h
lds dx,OldInt23 ;ctrl-C handler (an IRET)
mov ax,2523h
int 21h
lds dx,OldInt24 ;DOS crit err handler
mov ax,2524h
int 21h
pop ds ;restore data seg DS
assume ds:Cseg
; ------------ RESTORE SAVED BREAK CHECKING LEVEL
mov ax,3301h ;to set break check level
mov dl,OldBreak ;get saved break state
int 21h
; ------------ RESTORE DOS 3.X SPECIFIC SYSTEM INFO
test TsrMode,NewDos ;using DOS 3.x
jz Dos3Restored ;NO - jump if old DOS 2
mov dx,offset OldExtErr ;DS:DX=3 words of ext err
mov ax,5d0ah ;to set ext err info
int 21h
Dos3Restored:
; ------------ RESET InDosSet FLAG VALUE TO PREVENT UNSAFE INT28
or TsrMode,InDosClr ;now we only care that InDos=0
; ------------ DO WE WANT TO DE-INSTALL?
cmp GetOutFlag, 0 ;set in POPUP if user said
je RestoreTheRegs ;to remove ourselves
jmp DeInstall ;go die gracefully
; ------------ RESTORE USER REGS
RestoreTheRegs:
pop es
pop ds
pop di
pop si
pop bp
pop dx
pop cx
pop bx
pop ax
assume ds:nothing
; ------------ RETURN TO USER STACK
cli ;always CLI for the old chips
mov ss,OldSS ;restore SS
mov sp,OldSP ;restore SP
sti ;OK guys
ret
InitPopup endp
;==============================================================================
; Popup - POPUP USER ROUTINE
;
; ALL REGISTERS EXCEPT SS:SP AND DS MAY BE CHANGED.
; DS IS PRESET TO THE TSR DATA SEGMENT.
;
; NOTE: UPON ENTRY TO THIS ROUTINE ALL DOS FUNCTIONS MAY BE CALLED.
; IF POPUP WAS DONE ON INT28, WITH CritErr-1, ALL DOS FUNCTIONS
; THAT WOULD NORMALLY USE THE CONSOLE STACK, WILL GO TO THE CRITICAL
; STACK, HENCE PREVENTING FURTHER POPUP DURING THE DOS CALL.
; (HOWEVER, MOST TSRs WOULD NOT POPUP ANYWAY, SINCE InDos-2).
;
; ADDRESSES OF THE InDos AND CritErr ARE STORED IN THE DOUBLE WORDS
; InDosPtr AND CritErrPtr.
;
; AT ENTRY CritErr FLAG IS 0 (ZERO), InDos NO GREATER THAN 1 (ONE).
;
; THE SCREEN HAS ALREADY BEEN SAVED (BUT NOT CLEARED).
; IT WILL BE RESTORED AUTOMATICALLY UPON EXIT.
;
;==============================================================================
ImageHandle dw 0
ImageName db "C:\Memory.Img", 0 ;in root directory
NamePrompt db 13, 10
db "Enter name of program to execute: "
NamePromptLen equ $-NamePrompt
WaitMsg1 db 13, 10, 13, 10
db "Swapping out...."
WaitMsgLen1 equ $-WaitMsg1
KeyWaitMsg db 13, 10
db "Press a key to continue."
KeyWaitMsgLen equ $-KeyWaitMsg
WaitMsg2 db 13, 10, 13, 10
db "Swapping in...."
WaitMsgLen2 equ $-WaitMsg2
CRLFMsg db 13, 10
CRLFMsgLen equ $-CRLFMsg
PrevSS dw 0
PrevSP dw 0
PrevDS dw 0
PrevES dw 0
Partition dw 0
CmdLin db 80 dup(0)
Popup proc near
assume ds:Cseg, es:nothing, ss:nothing
; ------------ IF NO PROGRAM IS ACTIVE, DON'T SAVE 192K
CheckCurrPgm:
mov ax, cs ;point to our MCB
dec ax ;(is 16 bytes below PSP)
mov es, ax ;'Z' means last one
CheckThisMCB:
cmp Byte Ptr es:[0000], "Z" ;MCB signature byte
je DoLastMCB ;this is last in chain
cmp Byte Ptr es:[0000], "M" ;'M' is other possibility
je GotoNextMCB ;try the next one
jmp PopExit ;MCB's are corrupted.
GotoNextMCB:
add ax, es:[0003] ;length of this block
inc ax ;plus 1
mov es, ax ;points to next MCB
jmp CheckThisMCB
DoLastMCB:
mov ax, Word Ptr es:[0003] ;get the length
cmp ax, 12288 ;is it size we need? (paras)
ja DoThisBlock ;yep.
jmp PopExit ;nope; don't get fancy
DoThisBlock:
mov ApplPSP, 0 ;default to "it's free"
mov bx, Word Ptr es:[0001] ;get owner of block
cmp bx, 0 ;is it a free block?
jne SetupForSwap ;no, have to swap it out
jmp ExecIt ;free block (don't swap)
SetupForSwap:
mov ax, es
inc ax ;point up to PSP
mov ApplPSP, ax
; ------------ IF COMMAND.COM OWNS THAT BLOCK, FORGET IT.
mov ax, cs ;point to us
cmp ax, bx ;owner of block we want
jbe SwapMemory ;it's okay to use Command
call ErrBeep ;oh-oh, we can't do anything
jmp PopExit
; ------------ SAVE 192K OF UPPER MEMORY IN FILE "MEMORY.IMG"
SwapMemory:
call CLS
mov cx, 80
mov di, offset CmdLin
mov al, 0
rep stosb
mov ah, 40h
mov bx, 1
mov cx, NamePromptLen
mov dx, offset NamePrompt
int 21h
call CursorOn
mov ah, 3Fh
mov bx, 0
mov cx, 80
mov dx, offset CmdLin
inc dx
int 21h
mov CmdLin, al
mov si, offset CmdLin
add si, ax
mov Byte Ptr [si], 0
mov ah, 40h
mov bx, 1
mov cx, WaitMsgLen1
mov dx, offset WaitMsg1
int 21h
; ------------ FIGURE OUT HOW MUCH MEMORY THERE IS; SWAP TOP 192K
int 12h ;should return ax=640
sub ax, 192 ;subtract what we need
mov cx, 6 ;shift left 6 bits in order
shl ax, cl ;to get it in paragraphs
mov Partition, ax ;remember para addr for later
mov ah, 3Ch
mov dx, offset ImageName
mov cx, 0
int 21h ;create memory image file
jnc SaveTheHndl
call ErrBeep ;Error!
jmp PopExit ;unable to create file
SaveTheHndl:
mov ImageHandle, ax ;we'll need it later
Write192K:
mov PrevDS, ds ;we fool with ds quite a bit...
mov bx, ax ;we also need handle now
mov ah, 40h
mov cx, 0FFF0h ;unsigned 65520 quantity
mov dx, 0
mov ds, cs:Partition ;recall para addr from above
int 21h ;write 64K to disk (well, almost)
jc Error192 ;if I/O error...
cmp ax, cx ;disk full?
jne Error192
mov ax, ds
add ax, 4095 ;bump up to next segment
mov ds, ax
mov ah, 40h
int 21h ;write next 64K (little less, really)
jc Error192 ;if I/O error...
cmp ax, cx ;disk full?
jne Error192
mov ax, ds
add ax, 4095 ;final segment
mov ds, ax
mov ah, 40h
int 21h ;write last 64K segment
jc Error192 ;if I/O error...
cmp ax, cx ;disk full?
jne Error192
mov ah, 3Eh
int 21h ;now close it (196,560 bytes written)
mov ax, cs:PrevDS ;restore our ds
mov ds, ax
jmp FreeIt
Error192:
call ErrBeep ;do it twice here for id
call ErrBeep
mov ah, 3Eh ;close it and get out
int 21h
mov ds, cs:PrevDS ;who knows where it wound up??
jmp PopExit
; ------------ FREE THAT MEMORY SPACE
FreeIt:
push es
mov ax, 0
mov es, ax
mov Byte Ptr es:[050Fh], 0
pop es
mov bx, ApplPSP ;pretend we're in Appl.
mov ax,5000h ;to set PSP via DOS
int 21h ;set Application PSP
mov ax, ApplPSP
dec ax
mov es, ax ;look at memory control block
mov bx, word ptr es:[3] ;get current allocation
mov CurrentAlloc, bx ;and save it
sub bx, 12160 ;190K = 12160 paragraphs
mov es, ApplPSP ;block to be shrunk
mov ah, 4Ah ;setblock
int 21h ;we now have some free memory
jnc ExecIt
mov bx,OurPSP ;our current process in BX
mov ax,5000h ;to set PSP via DOS
int 21h ;restore our PSP
call ErrBeep ;memory request failed
call ErrBeep ;do twice
jmp PopExit ;let's get out of here
; ------------ RUN PROGRAM
ExecIt:
push ds
pop es
call CLS
mov bx,OurPSP ;our current process in BX
mov ax,5000h ;to set PSP via DOS
int 21h ;restore our PSP
mov cs:PrevES, es
mov cs:PrevDS, ds
mov cs:PrevSS, ss
mov cs:PrevSP, sp
mov si, offset CmdLin
Int 2Eh
CLI
mov ss, cs:PrevSS
mov sp, cs:PrevSP
mov ds, cs:PrevDS
mov es, cs:PrevES
STI
; ------------ GRAB THE MEMORY BACK (BY ALLOCATING ALL OF IT AGAIN)
ResetMemory:
cmp ApplPSP, 0 ;is application alive?
jne GetMain ;yes, re-alloc the memory
jmp PopExit ;no, no need to re-alloc
GetMain:
mov bx,ApplPSP ;pretend we're the appl.
mov ax,5000h ;to set PSP via DOS
int 21h ;restore our PSP
mov es, ApplPSP ;point to Appl's PSP
mov bx, CurrentAlloc ;what it used to have
mov ah, 4Ah ;ask for old amt. back
int 21h ;will DOS do it?
jnc RestoreImage ;yep, no error
call ErrBeep ;realloc-memory failed
call ErrBeep ;thrice
call ErrBeep ;and fall through restore
; ------------ RESTORE THE 192K MEMORY IMAGE FROM DISK
RestoreImage:
push ds
pop es
mov ah, 40h
mov dx, offset KeyWaitMsg
mov cx, KeyWaitMsgLen
mov bx, 1
int 21h
mov ax, 0
int 16h
mov ah, 40h
mov dx, offset WaitMsg2
mov cx, WaitMsgLen2
mov bx, 1
int 21h
mov bx,OurPSP ;our current process in BX
mov ax,5000h ;to set PSP via DOS
int 21h ;restore our PSP
mov ax, 3D00h ;"compatibility mode" open
mov dx, offset ImageName
int 21h
jnc Read192K
call ErrBeep ;a "whoops!"
jmp PopExit
Read192K:
mov ImageHandle, ax ;it may have changed
mov bx, ax
mov PrevDS, ds ;so we can mess with it
mov ah, 3Fh ;read back the image
mov ds, cs:Partition ;point to upper 192K
mov dx, 0
mov cx, 0FFF0h ;same segment size as before
int 21h ;read it
jc Error192r ;if I/O error...
cmp ax, cx ;not all bytes read?
jne Error192r
mov ax, ds
add ax, 4095 ;bump up to next segment
mov ds, ax
mov ah, 3Fh ;here we go again
int 21h
jc Error192r ;if I/O error...
cmp ax, cx ;not all bytes?
jne Error192r
mov ax, ds
add ax, 4095 ;bump up to last segment
mov ds, ax
mov ah, 3Fh ;for the last time
int 21h
jc Error192r ;if I/O error...
cmp ax, cx ;everything there?
jne Error192r
mov ah, 3Eh ;close it
int 21h
mov ds, cs:PrevDS ;set it back
jmp PopExit ;all done!
Error192r:
call ErrBeep ;3 times for id
call ErrBeep
call ErrBeep
mov ah, 3Eh ;close error file
int 21h ;restore ds
mov ds, cs:PrevDS ;and fall through to exit
; ------------ RETURN TO THE ORIGINAL APPLICATION
PopExit:
push ds
pop es
ret
; ------------ UNINSTALL DOESN'T APPLY HERE
UnInstall:
mov GetOutFlag, 1 ;will do it later
ret
Popup endp
;==============================================================================
; TSR IRON CURTAIN
;==============================================================================
TsrCurtain:
;***********************************************************
;==============================================================================
; NON-RESIDENT MESSAGES FOR INIT
;==============================================================================
SecondMsg label byte
db 7,13,10
db "The Context Switch program is already loaded!",13,10
db "$"
HotKeyMsg label byte
db 13,10,13,10
db "CONTEXT SWITCH"
db 13,10
db "Hit <Alt Right-Shift> to invoke Context Switch."
db 13,10,13,10
db "$"
Dos1Msg label byte
db 7,13,10
db "Must use DOS release 2.00 or later.",13,10,10
db "$"
BadDosMsg label byte
db 7,13,10
db "Did not recognize DOS version.",13,10,10
db "$"
; ------------ DOS ERROR LEVEL EXIT CODES
xOk equ 0 ;normal, OK exit
xSecond equ 1 ;TSR already loaded
xBadDos equ 2 ;CritErr flag not found
;==============================================================================
; DISPLAY BANNER, INITIALIZE SYSTEM DATA, CHECK IF ALREADY LOADED,
; HOOK INTO INTERRUPT CHAIN, TERMINATE, BUT STAY RESIDENT.
;==============================================================================
Init proc near
assume ds:Cseg, es:nothing, ss:nothing
call CLS
mov dx,0
call Gotoxy
; ------------ USE INT16H DIAGNOSTIC TO SEE IF TSR ALREADY INSTALLED
mov ax,GetId ;INT16h diagnostic request
int 16h ;now, AX=MyId if installed
cmp ax,MyId ;TSR already installed?
jne CheckDos ;NO - jump if not installed
; ------------ TSR ALREADY INSTALLED, DISPLAY MSG, EXIT
mov dx,offset SecondMsg
mov ah,9
int 21h ;display already-installed
mov dx,offset HotKeyMsg
mov ah,9
int 21h ;be kind & disp hot key
mov ax,4c00h + xSecond ;error level in AL
int 21h ;abort now
; ------------ It's only DOS 1! Too bad.
Dos1: mov dx,offset Dos1Msg
mov ah,9
int 21h ;display msg about DOS 1
int 20h ;no err level for DOS 1
; ------------ ENSURE DOS VERSION IS NEWER THAN 2.00
CheckDos: or TsrMode,NewDos ;assume suing DOS 3.x
mov ah,30h ;to get DOS version number
int 21h ;version is AL.AH
cmp al,2 ;release 2 or newer?
jb Dos1 ;NO - jump if DOS 1 in use
ja DosFlags ;jump if DOS 3.x
and TsrMode,not NewDos ;now, say we use DOS 2.x
; ------------ INITIALIZE PTRS TO DOS FLAGS - 1ST InDos
DosFlags: mov ax,3400h ;to get InDos ptr
int 21h ;ES:BX=seg:off of InDos
mov InDosOff,bx ;save ptr
mov InDosSeg,es
; ------------ WE NEED CritErr TO USE PSP FUNCTIONS IN DOS 2.X
xor dl,dl ;DL=0=this is 1st scan
mov CritErrSeg,es ;DOS seg still in ES
CritScan: mov di,bx ;start search at InDos
mov cx,2000h ;search max 1000h words
mov ax,3e80h ;opcode CMP BYTE PTR [CritErr]
cld ;better serach forward
CritScan2: repne scasw ;search till found or end
jne NoCritFound ;jump if CMP not found
;ES:[DI-2] at:
; CMP BYTE PTR [CritErr]
; JNZ ...
; MOV SP,stack addr
cmp byte ptr es:[di][5],0bch ;really CMP SP there?
jne CritScan2 ;NO - scan again if not
mov ax,word ptr es:[di] ;now, AX=CritErr offset
mov CritErrOff,ax ;save it
jmp short InitData ;OK to end this now
NoCritFound: or dl,dl ;was this1 st scan?
jnz BadDos ;NO - CritErr not founbd at all
inc dl ;DL=1=this is 2nd scan
inc bx ;try scan at odd/even offset
jmp CritScan ;scan again
; ------------ COULD NOT LOCATE DOS CritErr FLAG - THAT'S AN ERROR
BadDos: mov dx,offset BadDosMsg
mov ah,9
int 21h ;display msg about that
mov ax,4c00h + xBadDos ;err level in AL
int 21h ;OK to use 4C (DOS >= 2)
; ------------ INITIALIZE SYSTEM DATA VARIABLES
InitData: ;store position for stack
mov OurSP,TsrCurtain - ComEntry + 100h + StackSize
mov OurSS,cs ;stack seg is code seg
mov ax,5100h ;to get current PSP from DOS
int 21h ;PSP now in BX
mov OurPSP,bx ;save our PSP
mov ah,2fh ;to get current DTA from DOS
int 21h ;now, ES:BX=current DTA
mov OurDTAOff,bx ;save it
mov OurDTASeg,es
and KeyMode,not HotIsShift ;hotkey is not shift state
or TsrMode,InDosClr ;will prevent unsafe INT28s
; ------------ SAVE VECTORS FOR OUR MONITOR INTERRUPTS
mov ax,3508h ;BIOS timer0 tick handler
int 21h ;ES:BX=vector
mov OldInt08Off,bx
mov OldInt08Seg,es
mov ax,3509h ;BIOS kb HW handler
int 21h ;ES:BX=vector
mov OldInt09Off,bx
mov OldInt09Seg,es
mov ax,3513h ;BIOS disk I/O service
int 21h ;ES:BX=vector
mov OldInt13Off,bx
mov OldInt13Seg,es
mov ax,3516h ;BIOS kb read
int 21h ;ES:BX=vector
mov OldInt16Off,bx
mov OldInt16Seg,es
mov ax,3521h ;DOS functions dispatcher
int 21h ;ES:BX=vector
mov OldInt21Off,bx
mov OldInt21Seg,es
mov ax,3528h ;DOS idle hook
int 21h ;ES:BX=vector
mov OldInt28Off,bx
mov OldInt28Seg,es
mov ax,3523h ;Previous ^C handler
int 21h ;ES:BX=vector
mov PrevInt23Off,bx
mov PrevInt23Seg,es
; ------------ ESTABLISH IRET INT23 TO PREVENT BREAK DURING VECTOR FIX
mov dx,offset NopInt ;DS:DX=dummy vector to set
mov ax,2523h ;to set ^C handler through DOS
int 21h ;now, no break will occur
; ------------ SAVE VECTORS FOR OUR MONITOR INTERRUPTS
mov ax,2508h ;to set our INT08h handler
mov dx,offset OurInt08 ;DS:DX=new vector
int 21h ;let DOS set vector
mov ax,2509h ;to set our INT09h handler
mov dx,offset OurInt09 ;DS:DX=new vector
int 21h ;let DOS set vector
mov ax,2513h ;to set our INT13h handler
mov dx,offset OurInt13 ;DS:DX=new vector
int 21h ;let DOS set vector
mov ax,2516h ;to set our INT16h handler
mov dx,offset OurInt16 ;DS:DX=new vector
int 21h ;let DOS set vector
mov ax,2521h ;to set our INT21h handler
mov dx,offset OurInt21 ;DS:DX=new vector
int 21h ;let DOS set vector
mov ax,2528h ;to set our INT28h handler
mov dx,offset OurInt28 ;DS:DX=new vector
int 21h ;let DOS set vector
mov word ptr savearea+2, ds ;seg of scrnhold
mov ax, offset scrnhold ;ofs of scrnhold
mov word ptr savearea, ax ;for scrn-save
mov ah, 15
int 10h ;get current video mode
cmp al, 7 ;if BW, then we need
jne IsColor
mov word ptr videocard + 2, 0B000h ;to set video address
mov word ptr videocard, 0 ;to B000:0000
mov videoflag, 1 ;and remember mode
jmp SayHello
IsColor:
mov word ptr videocard + 2, 0B800h ;if color mode, then
mov word ptr videocard, 0 ;address is B800:0000
mov videoflag, 0 ;remember we're in color
; ------------ DISLAY MSG ABOUT HOW WELL THIS IS ALL RUNNING
SayHello:
mov dx,offset HotKeyMsg
mov ah,9
int 21h ;disp hot key msg.
mov dx,(TsrCurtain-ComEntry+100h+StackSize+15) SHR 4
mov ax,3100h + xOk ;TSR, AL=err level
int 21h
Init endp
;==============================================================================
Cseg ends
end ComEntry
===============================================================
Cseg ends
end ComEntry